#include<iostream>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
void SIGCHLD_hander(int sig){
    while(1){//回收当前所有的僵尸子进程
        //WNOHANG 不让父进程阻塞,收到信号就回收当前所有需要回收的
        //为什么要用循环和waitpid的返回值控制,不收到一个信号回收一个僵尸??
        //因为如果几个子进程同时退出,抵达一个信号,阻塞一个信号,其他信号就丢失了
        //故,收到一次信号需要回收当前所有的僵尸子进程,就算丢失了某些进程的信号也能正确回收它们
        //回收之后返回做自己的事情,等待信号
        pid_t wait_ret=waitpid(-1,nullptr,WNOHANG);
        if(wait_ret>0){
            cout<<"成功回收子进程 pid: "<<wait_ret<<endl;
            continue;
        }
        if(wait_ret==0){//还有子进程在运行,还是回去做自己的工作,等待信号
            cout<<"已经没有需要回收的子进程,我回去做自己的事情了!"<<endl;
            break;
        }
        if(wait_ret<0){//返回0代表没有子进程了
            cout<<"所有子进程已经回收完毕!"<<endl;
            exit(0);
        }
    }       
} 
int main(){
    // signal(SIGCHLD,SIG_IGN);//虽然默认方法就算忽略,但若手动绑定,os会做特殊处理,使得子进程退出不给父进程发信号,并且退出后直接被回收!
    // signal(SIGCHLD,SIGCHLD_hander);
    //sigaction 可以设置处理信号时自动阻塞其他信号
    //使得回收僵尸进程完毕后 再处理其他信号
    struct sigaction waitpid_act;
    waitpid_act.sa_flags=0;
    waitpid_act.sa_restorer=nullptr;
    sigset_t waitpid_hander_mask;
    sigfillset(&waitpid_hander_mask);
    waitpid_act.sa_mask=waitpid_hander_mask;
    waitpid_act.sa_handler=SIGCHLD_hander;
    sigaction(SIGCHLD,&waitpid_act,nullptr);
    pid_t fork_ret=1;
    int i=0;
    for(i=0;i<10;i++){
        fork_ret=fork();
    if(fork_ret==0){
        //child
        //(1) 5个进程直接一起退出,看能否全部被回收
        //(2) 另外5个等会儿一起退出,看退出前父进程能否继续做自己的事情
        //(3) 观察收到信号后能否再返回来回收剩余的5个进程
        //(4) 观察能否正确识别,是否还有未退出的子进程
        if(i>5){
            sleep(8);
        }
        sleep(1);
        exit(0);
    }
    else
        cout<<"创建子进程成功 pid: "<<fork_ret<<endl;

    }
    while(1){
        cout<<"我是父进程,我在做自己的事情"<<endl;
        sleep(2);
    }
    return 0;
}